package work.linruchang.mybatisplussamplestest.config.mybatisplus;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.HashUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.*;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.StringValue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import work.linruchang.mybatisplussamplestest.bean.FriendlyLink;
import work.linruchang.mybatisplussamplestest.bean.SysUser;
import work.linruchang.mybatisplussamplestest.config.mybatisplus.injector.DeleteByIdLogic;
import work.linruchang.mybatisplussamplestest.config.mybatisplus.injector.DeleteByIdPhysical;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * 作用：
 *
 * @author LinRuChang
 * @version 1.0
 * @date 2022/06/12
 * @since 1.8
 **/
@Configuration
public class MyBatisPlusConfig {



    /**
     * 主键生成策略，当com.baomidou.mybatisplus.annotation.TableId#type() 为 IdType.INPUT
     *
     * @return
     */
    @Bean
    public IKeyGenerator keyGenerator() {
        return new IKeyGenerator() {

            @Override
            public String executeSql(String incrementerName) {
                incrementerName = StrUtil.nullToEmpty(StrUtil.trim(incrementerName));
                return StrUtil.format("select CONCAT('{}',SUBSTR(REPLACE(uuid(),'-',''),1,32-LENGTH('{}')))", incrementerName, incrementerName);
            }

            @Override
            public DbType dbType() {
                return DbType.MYSQL;
            }
        };
    }


    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

        //动态表单
        interceptor.addInnerInterceptor(new DynamicTableNameInnerInterceptor(() -> {
            Console.log("动态表单执行成功");
        }, (sql, tableName) -> {
            Console.log("当前执行的SQL：{}\n 表名：{}", sql, tableName);

            boolean needDynamicFlag = Arrays.asList(SysUser.class).stream()
                    .map(TableInfoHelper::getTableInfo)
                    .map(TableInfo::getTableName)
                    .anyMatch(t -> StrUtil.equals(t, tableName));
            if(!needDynamicFlag) {return tableName;}

            Map<String, Object> requestData = RequestDataHelper.getRequestData();

            String tableNum = null;
            if (CollUtil.isNotEmpty(requestData)) {
                String mapValueContent = requestData.values()
                        .stream()
                        .map(Object::toString)
                        .reduce(StrUtil.EMPTY, (t1, t2) -> {
                            return StrUtil.join(StrUtil.DOT, t1, t2);
                        });
                int mapHashValue = HashUtil.fnvHash(mapValueContent);
                int modulus = mapHashValue % 3;
                tableNum = String.valueOf(modulus);
                Console.log("当前参数的Hash值：{}: 取模后：{}", mapHashValue, modulus);
            }


            String resultTableName = tableName;
            if (StrUtil.isNotBlank(tableNum)) {
                resultTableName = StrUtil.format("{}_{}", tableName, tableNum);
                RequestDataHelper2.setRequestData(resultTableName, TableInfoHelper.getTableInfo(tableName));
            }

            return resultTableName;
        }));

        //乐观锁，字段添加version注解
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());


        //防全表更新与删除插件
        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());

        //租户模式
        interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {

            /**
             * 租户字段的值  每执行一次SQL一次都会判断一次
             * @return
             */
            @Override
            public Expression getTenantId() {
                Object tenant = RequestDataHelper.getRequestData("tenant");
                return tenant == null ? null : new StringValue(tenant.toString());
            }

            /**
             * 租户的字段名
             * @return
             */
            @Override
            public String getTenantIdColumn() {
                return "tenant";
            }


            /**
             * 某表的SQL是否忽略带上租户字段  每执行一次SQL一次都会判断一次
             * @param tableName 表名  这里的表名可能是动态表单的表名注意区别
             * @return
             */
            @Override
            public boolean ignoreTable(String tableName) {

                //无租户值则直接忽略拼接租户字段
                Object tenant = RequestDataHelper.getRequestData("tenant");
                boolean tenantValueFlag = ObjectUtil.isNotEmpty(tenant);

                //根据实体是否定义tenant属性来评判是否需要租户条件 == 经过动态表单的表名可能非com.baomidou.mybatisplus.annotation.TableName.value定义的表名
                TableInfo tableInfo = ObjectUtil.defaultIfNull(TableInfoHelper.getTableInfo(tableName),(TableInfo)RequestDataHelper2.getRequestData(tableName));
                boolean tanantEntityFlag = tableInfo.getFieldList().stream()
                        .anyMatch(tableFieldInfo -> {
                            return StrUtil.equals(tableFieldInfo.getColumn(), "tenant");
                        });


                //需要租户的表
                return !tanantEntityFlag || !tenantValueFlag || !Arrays.asList(SysUser.class, FriendlyLink.class).stream()
                        .map(TableInfoHelper::getTableInfo)
                        .map(TableInfo::getTableName)
                        .anyMatch(t -> StrUtil.contains(tableName,t));
            }
        }));


        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
        paginationInnerInterceptor.setDbType(DbType.MYSQL);
        interceptor.addInnerInterceptor(paginationInnerInterceptor);




        return interceptor;
    }


    /**
     * SQL注入器
     *
     * @return
     */
    @Bean
    public ISqlInjector enhanceISqlInjector() {
        return new DefaultSqlInjector() {
            @Override
            public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
                List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);

                methodList.add(new DeleteByIdPhysical());
                methodList.add(new DeleteByIdLogic());


                return methodList;
            }
        };
    }

}
